package puzzle.slilin2.solver;

import puzzle.common.data.HatanException;
import puzzle.common.point.Direction;
import puzzle.common.point.Pnt;
import puzzle.common.point.Vct;
import puzzle.slilin2.data.CellType;
import puzzle.slilin2.data.LineCellState;
import puzzle.slilin2.data.NumberCellState;
import puzzle.slilin2.data.SlilinBoard;

class NumberCornerRule extends SlilinCellRule {

	@Override
	protected CellType appliedCellType() {
		return CellType.NUMBER;
	}

	@Override
	protected boolean skipOutside() {
		return true;
	}

	@Override
	protected boolean skipUnmodifiedCell() {
		return true;
	}

	public boolean apply(SlilinBoard b, Pnt p0) throws HatanException {
		if (b.isNumberFixed(p0)) return false;

		boolean isChanged = false;
		switch (b.getNumber(p0)) {
		case NumberCellState.EMPTY:
			isChanged |= processEmpty(b, p0, Direction.N.v);
			isChanged |= processEmpty(b, p0, Direction.E.v);
			isChanged |= processEmpty(b, p0, Direction.S.v);
			isChanged |= processEmpty(b, p0, Direction.W.v);
			break;
		case 1:
			isChanged |= processOne(b, p0, Direction.N.v);
			isChanged |= processOne(b, p0, Direction.E.v);
			isChanged |= processOne(b, p0, Direction.S.v);
			isChanged |= processOne(b, p0, Direction.W.v);
			break;
//		case 2:
//			isChanged |= processTwo(b, p0, Direction.N.v);
//			isChanged |= processTwo(b, p0, Direction.E.v);
//			isChanged |= processTwo(b, p0, Direction.S.v);
//			isChanged |= processTwo(b, p0, Direction.W.v);
//			break;
		case 3:
			isChanged |= processThree(b, p0, p0, Direction.N.v);
			isChanged |= processThree(b, p0, p0, Direction.E.v);
			isChanged |= processThree(b, p0, p0, Direction.S.v);
			isChanged |= processThree(b, p0, p0, Direction.W.v);
			break;
		}
		return isChanged;
	}

	private boolean processEmpty(SlilinBoard b, Pnt p0, Vct v) {
		Vct vl = v.lrot();
		Pnt pc = p0.add(v).add(vl);
		Pnt p1 = pc.add(v);
		Pnt p2 = pc.add(vl);
		LineCellState s1 = b.getLineCellState(p1);
		LineCellState s2 = b.getLineCellState(p2);

		boolean isChanged = false;
		if (s1.isForbidden() && s2.isForbidden()) {
			if (b.getLineCellState(p0.add(vl, -1)).exists()
					&& b.getLineCellState(p0.add(v, -1)).exists()) {
				isChanged |= b.getLineCellState(p0.add(v)).setForbidden(true);
				isChanged |= b.getLineCellState(p0.add(vl)).setForbidden(true);
			}
			if (b.getLineCellState(p1.add(vl, -2)).isForbidden() &&
					b.getLineCellState(p2.add(vl, -4)).isForbidden() &&
					b.getLineCellState(p1.add(v, -4)).isForbidden() &&
					b.getLineCellState(p2.add(v, -2)).isForbidden()) {
				isChanged |= b.getLineCellState(p0.add(v)).setForbidden(true);
				isChanged |= b.getLineCellState(p0.add(vl)).setForbidden(true);
				isChanged |= b.getLineCellState(p0.add(v, -1)).setForbidden(true);
				isChanged |= b.getLineCellState(p0.add(vl, -1)).setForbidden(true);
			}
		}
		return isChanged;
	}

	private boolean processOne(SlilinBoard b, Pnt p0, Vct v) {
		Vct vl = v.lrot();
		Pnt p1 = p0.add(v).add(vl);
		LineCellState s1 = b.getLineCellState(p1.add(v));
		LineCellState s2 = b.getLineCellState(p1.add(vl));

		boolean isChanged = false;
		if (s1.isFixed() && s2.isFixed()) {
			if (s1.isForbidden() && s2.isForbidden()) {
				isChanged |= b.getLineCellState(p0.add(v)).setForbidden(true);
				isChanged |= b.getLineCellState(p0.add(vl)).setForbidden(true);
			} else if (s1.exists() && s2.exists()) {
				// nothing to do
			} else {
				/*
				 * isChanged |= b.getLineCellState(p0.add(v.inv
				 * ())).setForbidden( true); isChanged |= b.getLineCellState
				 * (p0.add(vl.inv())).setForbidden( true);
				 */
			}
		}
		return isChanged;
	}

	private boolean processThree(SlilinBoard b, Pnt p0, Pnt p, Vct v) throws HatanException {
		Vct vl = v.lrot();
		Pnt p1 = p.add(v).add(vl);
		LineCellState s1 = b.getLineCellState(p1.add(v));
		LineCellState s2 = b.getLineCellState(p1.add(vl));

		boolean isChanged = false;
		if (s1.isForbidden() && s2.isForbidden()) {
			isChanged |= b.getLineCellState(p0.add(v)).setExists(true);
			isChanged |= b.getLineCellState(p0.add(vl)).setExists(true);
		} else if (s1.exists() || s2.exists()) {
			isChanged |= b.getLineCellState(p0.add(v, -1)).setExists(true);
			isChanged |= b.getLineCellState(p0.add(vl, -1)).setExists(true);
		} else if (s1.exists() && s2.exists()) {
			throw new HatanException();
		} else {
			p = p1.add(v).add(vl);
			if (b.getNumber(p) == 2) {
				isChanged |= processThree(b, p0, p, v);
			}
		}

		return isChanged;
	}

}
